package com.dream.interceptor;

import com.dream.annotation.OnlyQuery;
import com.dream.exception.ApiException;
import com.dream.service.AppKeyService;
import com.dream.utils.SignUtils;
import com.dream.wrapper.CachedBodyHttpServletRequest;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.dream.enums.ErrorEnums.*;

@Slf4j
@Component
public class ApiSecurityInterceptor implements HandlerInterceptor {

    @Autowired
    private AppKeyService appKeyService;
    private final ObjectMapper objectMapper = new ObjectMapper();

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 接口请求有效期
     */
    private static final long EXPIRATION_TIME_MILLIS = 5 * 60 * 1000;

    /**
     * 限流请求时间，单位秒
     * 默认10秒
     */
    private static final long RATE_LIMIT_TIME_MILLIS = 10;

    /**
     * 单位时间内允许的请求次数
     */
    private static final int RATE_LIMIT_COUNT = 5;

    // 删除原有的 init 方法，使用 Redis 自动过期机制

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
        CachedBodyHttpServletRequest cachedRequest = (CachedBodyHttpServletRequest) request;
        // 获取并规范化请求路径
        String requestURI = request.getRequestURI();
        String normalizedUri = normalizePath(requestURI);

        // 接口限流校验
        String repeatKey = "rate_limit:" + normalizedUri + ":" + getClientIP(request);
        Long count = stringRedisTemplate.opsForValue().increment(repeatKey);
        if (count != null && count == 1) {
            stringRedisTemplate.expire(repeatKey, RATE_LIMIT_TIME_MILLIS, TimeUnit.SECONDS);
        }

        if (count != null && count > RATE_LIMIT_COUNT) {
            throw new ApiException(LIMIT_ERROR.getCode(), LIMIT_ERROR.getMessage());
        }

        // 检查处理器方法是否带有 OnlyQuery 注解，如果带有该注解，则不进行签名校验
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            OnlyQuery onlyQuery = handlerMethod.getMethodAnnotation(OnlyQuery.class);
            if (onlyQuery != null) {
                return true;
            }
        }

        String appId = request.getHeader("appId");
        String timestamp = request.getHeader("timestamp");
        String nonce = request.getHeader("nonce");
        String sign = request.getHeader("sign");
        // 接口参数校验
        if (!StringUtils.hasText(appId) || !StringUtils.hasText(timestamp) ||
                !StringUtils.hasText(nonce) || !StringUtils.hasText(sign)) {
            throw new ApiException(SING_MISS_PARAM_ERROR.getCode(), SING_MISS_PARAM_ERROR.getMessage());
        }

        // appId校验
        String secret = appKeyService.getSecretByAppId(appId);
        if (secret == null) {
            throw new ApiException(SING_APPID_ERROR.getCode(), SING_APPID_ERROR.getMessage());
        }

        // 请求时间过期校验
        long now = System.currentTimeMillis();
        long ts = Long.parseLong(timestamp);
        if (Math.abs(now - ts) > EXPIRATION_TIME_MILLIS) {
            throw new ApiException(EXPIRATION_TIME_ERROR.getCode(), EXPIRATION_TIME_ERROR.getMessage());
        }

        Map<String, String> params = collectAllParams(cachedRequest);

        // 重复请求校验
        String key = appId + ":" + nonce + ":" + SignUtils.md5(params.toString());
        Boolean result = redisTemplate.opsForValue().setIfAbsent(key, now, EXPIRATION_TIME_MILLIS,
                TimeUnit.MILLISECONDS);
        if (result == null || !result) {
            throw new ApiException(REPEAT_ERROR.getCode(), REPEAT_ERROR.getMessage());
        }
        params.put("appId", appId);
        params.put("timestamp", timestamp);
        params.put("nonce", nonce);

        // 签名校验
        String serverSign = SignUtils.sign(params, secret);
        if (!serverSign.equalsIgnoreCase(sign)) {
            throw new ApiException(SIGN_ERROR.getCode(), SIGN_ERROR.getMessage());
        }

        return true;
    }

    private Map<String, String> collectAllParams(CachedBodyHttpServletRequest request) {
        Map<String, String> map = new HashMap<>();
        try {
            // 获取请求体内容
            String body = new String(request.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
            if (StringUtils.hasText(body))  {
                if (StringUtils.hasText(body)) {
                    try {
                        Map<String, Object> json = objectMapper.readValue(body, Map.class);
                        for (Map.Entry<String, Object> entry : json.entrySet()) {
                            map.put(entry.getKey(), entry.getValue() == null ? "" : entry.getValue().toString());
                        }
                    } catch (Exception e) {
                        log.error("Failed to parse request body: {}", body, e);
                        throw new ApiException(REQUEST_BODY_ERROR.getCode(), REQUEST_BODY_ERROR.getMessage());
                    }
                }
            } else {
                // 如果没有请求体，则获取URL参数
                Enumeration<String> parameterNames = request.getParameterNames();
                while (parameterNames.hasMoreElements()) {
                    String name = parameterNames.nextElement();
                    String value = request.getParameter(name);
                    map.put(name, value);
                }
            }
        } catch (Exception e) {
            log.error("Failed to read request body", e);
            throw new ApiException(REQUEST_BODY_ERROR.getCode(), REQUEST_BODY_ERROR.getMessage());
        }
        return map;
    }

    private String getClientIP(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        return (ip == null || ip.isEmpty()) ? request.getRemoteAddr() : ip.split(",")[0];
    }

    private String normalizePath(String path) {
        // 移除重复的斜杠
        String normalized = path.replaceAll("/+", "/");
        // 确保路径以单个斜杠开头
        if (!normalized.startsWith("/")) {
            normalized = "/" + normalized;
        }
        // 移除末尾的斜杠（除非是根路径）
        if (normalized.length() > 1 && normalized.endsWith("/")) {
            normalized = normalized.substring(0, normalized.length() - 1);
        }
        return normalized;
    }
}